import { UICanvas } from "./lib/ui.canvas.js";
import { UIDiv } from "./lib/ui.js"
// import { Layers } from "./Layers.js";
import { Rect } from "./Shape.Rect.js";
import { captureMouse, containsPoint } from './utils/index.js';
import { Pencil } from './Shape.Pencil.js';
import { Point } from './Point.js';
import { TextParagraph } from './Text.Paragraph.js';
import { EventDispatcher } from "./EventDispatcher.js";
import { TextCursor } from "./Text.Cursor.js";

class Viewer {
    constructor(editor) {
        const container = new UIDiv();
        container.addClass('Viewer');
        container.setId('viewer');

        const mouse = captureMouse(container.dom);
        const mainCanvas = new UICanvas();
        mainCanvas.mouse = mouse;

        container.add(mainCanvas);

        this.dom = container.dom;
        this.mainCanvas = mainCanvas;
        this.editor = editor;

        this.layers = editor.layers;
        this.mouse = mouse;

        // this.textCursor = new TextCursor(0, 0)

        this.startSelect = false
        this.startSelectMouseDownX = 0
        this.startSelectMouseDownY = 0

        this.setup();
    }
    setup() {

        // 抓手工具拖动中
        this.handDragging = false;


        // 开始拖动元素前鼠标与元素的位置
        this._offsetX = 0;
        this._offsetY = 0;

        // 抓手工具拖动前的位移
        this._dx = 0;
        this._dy = 0;

        this.runLoop();
        this.setupInput();
    }

    runLoop() {
        // resize
        this.canvasResize();
        // clear
        this.mainCanvas.clearCanvas();
        // update
        this.update();
        // draw
        this.draw();

        requestAnimationFrame(() => {
            this.runLoop()
        });
    }
    setupInput() {
        const mouse = this.mouse;
        const layers = this.layers;
        const editor = this.editor;
        const self = this;
        const context = this.mainCanvas.context;

        window.addEventListener('keydown', (event) => {
            const state = editor.state;
            if (event.key == ' ' && state != 'text') {
                editor.setState('hand');
            }
            if (state == 'text') {
                this.handleTextKeyDown(event);
            }
        });

        window.addEventListener('keyup', (event) => {

            const state = editor.state;

            if (event.key == ' ' && state != 'text') {
                editor.setState('move');
            }
            else if (event.key == 'Delete' || event.key == 'Backspace') {
                if (layers._sel) {
                    layers.remove(layers._sel);
                }
            }
            else if (event.key == ']' && event.ctrlKey) {
                if (layers._sel) {
                    layers.moveUp(layers._sel);
                }
            }
            else if (event.key == '[' && event.ctrlKey) {
                if (layers._sel) {
                    layers.moveDown(layers._sel);
                }
            }

            // if (state == 'text') {
            //     this.handleTextKeyUp(event);
            // }


        });

        // let drawingElement = null;

        let mouseDownX = 0;
        let mouseDownY = 0;
        let dx = 0;
        let dy = 0;

        this.dom.addEventListener('mousedown', (event) => {


            const state = editor.state;
            const { startOffsetX, startOffsetY } = this.mainCanvas;

            mouseDownX = mouse.x;
            mouseDownY = mouse.y;

            dx = mouse.x - startOffsetX;
            dy = mouse.y - startOffsetY;

            if (state == 'move') {
                // this.startDragPen();
                layers.unSelectAll();
                this.editor.signals.objectSelected.dispatch(null);
                if (this.textParagraph) {
                    this.textParagraph.textCursor = null
                }
                console.log('move down');

                let elem = this.findHoverElement()

                /* 开始区域选择 */
                if (elem === null) {
                    this.startSelect = true;
                    this.startSelectMouseDownX = mouse.x
                    this.startSelectMouseDownY = mouse.y
                }

                this.eventHandle('mousedown', event);
            }

            if (state == 'shape') {
                this.addRect();
            }

            else if (state == 'hand') {
                this.handDragging = true;
            }

            else if (state == 'pen') {
                this.addPen();
            }

            else if (state == 'text') {
                this.addTextPragraph();
            }

        });

        this.dom.addEventListener('mouseup', (event) => {
            this.eventHandle('mouseup', event);

            this.rect = null;
            this.pen = null;
            // this.textParagraph = null;
            this.handDragging = false;

            if (layers._sel) {
                layers._sel._drag = false;
            }


            const state = this.editor.state;

            if (state == 'shape') {
                this.editor.setState('move');
            }

            else if (state == 'pen') {

            } else if (state == 'move') {
                this.startSelect = false;
                this.layers.remove(this.selectBox)
                this.selectBox = null
            }
        });

        this.dom.addEventListener('mousemove', (event) => {
            const state = this.editor.state;
            const { startOffsetX, startOffsetY } = this.mainCanvas;

            if (state == 'shape') {
                this.setupRect(dx + 0.5, dy + 0.5, mouse.x - mouseDownX, mouse.y - mouseDownY);
            }

            else if (state == 'move') {
                if (this.startSelect) {
                    this.showSelectBox()
                } else {
                    this.handleMouseMove();
                }
            }

            else if (state == 'hand') {
                if (this.handDragging) {
                    this.setStartOffset(mouse.x - dx, mouse.y - dy);
                }
            }

            else if (state == 'pen') {
                if (this.pen) {
                    this.pen.addPoint(new Point(mouse.x - startOffsetX, mouse.y - startOffsetY))
                }
            }

        });

        this.dom.addEventListener('click', (event) => {
            const state = this.editor.state;
            if (state == 'move') {
                this.eventHandle('click', event);
            }
        });

        this.dom.addEventListener('contextmenu', (event) => {
            event.preventDefault();
            this.eventHandle('contextmenu', event);
        })
    }

    handleTextKeyDown(event) {
        const context = this.mainCanvas.context;

        if (event.key == 'Enter') {
            this.textParagraph.insertLine(context);
        }
        else if (event.key == 'Backspace') {
            this.textParagraph.backspace();
        }
        else if (event.key == 'Control') {
            event.preventDefault()
        }
        else if (event.key == 'Meta') {
            event.preventDefault()
        }
        else if (event.key == 'Alt') {
            event.preventDefault()
        }
        else if (event.key == 'ArrowDown') {
            event.preventDefault()
            this.textParagraph.downCursor()
        }
        else if (event.key == 'ArrowUp') {
            event.preventDefault()
            this.textParagraph.upCursor()
        }
        else if (event.key == 'ArrowLeft') {
            event.preventDefault()
            this.textParagraph.backwardCursor()
        }
        else if (event.key == 'ArrowRight') {
            event.preventDefault()
            this.textParagraph.forwardCursor()
        }
        else if (event.key == 'Home') {
            event.preventDefault()
            this.textParagraph.homeCursor()
        }
        else if (event.key == 'End') {
            event.preventDefault()
            this.textParagraph.endCursor()
        }
        else {
            this.textParagraph.insertText(event.key)
        }
    }
    // handleTextKeyUp(event) {
    //     this.textParagraph.insertText(event.key)
    // }

    // handleRubber(type) {
    //     let sel = this.layers.getSelectedElement();
    //     if (sel != null) {
    //         let rect = sel.getBoundingBox(true);
    //         if (type == 'mousedown') {
    //             console.log(rect);
    //         }
    //     }
    // }

    // 此处进行元素的 resize 操作
    handleResizeElement(elem) {
        if (!elem) {
            return;
        };

        const mouse = this.mouse;
        const rubber = elem.rubber;
        const self = this;

        let offsetX = mouse.x - elem.x;
        let offsetY = mouse.y - elem.y;

        let mouseDownX = mouse.x;
        let mouseDownY = mouse.y;
        let mouseDownW = elem.width;
        let mouseDownH = elem.height;

        let { context, startOffsetX, startOffsetY } = this.mainCanvas;
        let cursorPos = rubber.mouseIn(mouse.x - startOffsetX, mouse.y - startOffsetY);

        // console.log('cursorPos', cursorPos);

        if (cursorPos == 'center') {
            console.log('move');
            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', () => {
                document.removeEventListener('mousemove', onMouseMove);
            });
        }

        // 右
        else if (cursorPos == 'er') {
            console.log('move er');
            document.addEventListener('mousemove', moveER);
            document.addEventListener('mouseup', () => {
                document.removeEventListener('mousemove', moveER);
            });
        }
        // 左
        else if (cursorPos == 'el') {
            console.log('move el');
            document.addEventListener('mousemove', moveEL);
            document.addEventListener('mouseup', () => {
                document.removeEventListener('mousemove', moveEL);
            });
        }

        // 上
        else if (cursorPos == 'et') {
            console.log('move et');
            document.addEventListener('mousemove', moveET);
            document.addEventListener('mouseup', () => {
                document.removeEventListener('mousemove', moveET);
            });
        }
        // 下
        else if (cursorPos == 'eb') {
            console.log('move eb');
            document.addEventListener('mousemove', moveEB);
            document.addEventListener('mouseup', () => {
                document.removeEventListener('mousemove', moveEB);
            });
        }
        // 右上
        else if (cursorPos == 'rt') {
            console.log('move rt');
            document.addEventListener('mousemove', moveRT);
            document.addEventListener('mouseup', () => {
                document.removeEventListener('mousemove', moveRT);
            });
        }
        // 右下
        else if (cursorPos == 'rb') {
            console.log('move rb');
            document.addEventListener('mousemove', moveRB);
            document.addEventListener('mouseup', () => {
                document.removeEventListener('mousemove', moveRB);
            });
        }
        // 左上
        else if (cursorPos == 'lt') {
            console.log('move lt');
            document.addEventListener('mousemove', moveLT);
            document.addEventListener('mouseup', () => {
                document.removeEventListener('mousemove', moveLT);
            });
        }
        // 左下
        else if (cursorPos == 'lb') {
            console.log('move lb');
            document.addEventListener('mousemove', moveLB);
            document.addEventListener('mouseup', () => {
                document.removeEventListener('mousemove', moveLB);
            });
        }


        /* move */
        function onMouseMove(e) {
            let left = mouse.x - offsetX;
            let top = mouse.y - offsetY;
            elem.x = left;
            elem.y = top;
            self.editor.signals.objectChanged.dispatch(elem);
        }

        /* move edge */
        function moveER(e) {
            let dx = mouse.x - mouseDownX;
            let w = mouseDownW + dx;
            elem.width = w;
            self.editor.signals.objectChanged.dispatch(elem);
        }
        function moveEL(e) {
            let dx = mouseDownX - mouse.x;
            let x = mouseDownX - dx - startOffsetX;
            let w = mouseDownW + dx;
            elem.width = w;
            elem.x = x;
            self.editor.signals.objectChanged.dispatch(elem);
        }
        function moveET(e) {
            let dy = mouseDownY - mouse.y;
            let y = mouseDownY - dy - startOffsetY;
            let h = mouseDownH + dy;
            elem.y = y;
            elem.height = h;
            self.editor.signals.objectChanged.dispatch(elem);
        }

        function moveEB(e) {
            let dy = mouse.y - mouseDownY;
            let h = mouseDownH + dy;
            elem.height = h;
            self.editor.signals.objectChanged.dispatch(elem);
        }
        /* move point */
        function moveRT(e) {
            moveER(e);
            moveET(e);
        }
        function moveRB(e) {
            moveER(e);
            moveEB(e);
        }
        function moveLT(e) {
            moveEL(e);
            moveET(e);
        }
        function moveLB(e) {
            moveEL(e);
            moveEB(e);
        }

    }

    /**
     * 根据Viewer的尺寸设置canvas的尺寸
     */
    canvasResize() {
        let { width, height } = this.dom.getBoundingClientRect();
        this.mainCanvas.setWidth(width);
        this.mainCanvas.setHeight(height);
    }

    update() {
        // 更新可见元素
        let { context, startOffsetX, startOffsetY } = this.mainCanvas;
        this.layers.traverse((e) => {
            e.update && e.update(context, startOffsetX, startOffsetY);
        });
        this.updateCursor();
    }

    draw() {

        // 网格
        if (this.editor.viewGrid) {
            this.mainCanvas.drawGrid();
        }

        let { context, startOffsetX, startOffsetY } = this.mainCanvas;

        // 绘制所有可见元素
        this.layers.traverse((e) => {
            e.draw(context, startOffsetX, startOffsetY);
        });

        // 标尺
        if (this.editor.viewRule) {
            this.mainCanvas.drawRule();
        }
        // this.mainCanvas.drawCrossLine();
    }

    updateCursor() {
        let cursorTable = {
            hand: 'url(img/cursor-hand.png) 4 4, auto',
            move: 'url(img/cursor-default.png) 4 4, auto',
        };

        let position = {
            rt: 'ne-resize',
            rb: 'se-resize',
            lt: 'nw-resize',
            lb: 'ne-resize',
            et: 'n-resize',
            eb: 's-resize',
            el: 'w-resize',
            er: 'e-resize',
            center: null
        };

        // update cursorPosition
        let cursorPosition = null;
        let mouse = this.mouse;
        let { startOffsetX, startOffsetY } = this.mainCanvas;

        let _sel = this.layers._sel;

        if (_sel && _sel.rubber) {
            cursorPosition = _sel.rubber.mouseIn(mouse.x - startOffsetX, mouse.y - startOffsetY);
        }

        let state = this.editor.state;
        let cursor = cursorTable[state] || 'default';
        if (cursorPosition != null) {
            cursor = position[cursorPosition] || 'default';
        }
        this.dom.style.cursor = cursor;
    }

    /**
     * 找出鼠标所在位置的最上层一个元素
     */
    findHoverElement() {
        let { context, startOffsetX, startOffsetY } = this.mainCanvas;
        const mouse = this.mouse;

        let hoverEls = this.layers.findHoverElements(context, mouse.x - startOffsetX, mouse.y - startOffsetY);

        if (hoverEls.length > 0) {
            return hoverEls[hoverEls.length - 1];
        }

        return null;

    }

    handleMouseMove() {
        let cur = this.findHoverElement();

        if (cur != null && cur instanceof EventDispatcher) {
            cur.dispatchEvent({ type: 'mousemove' });
        }
        // 模拟出 mouseover 和 mouseout
        this.layers.traverse(e => {
            if (e == cur) {
                if (!e.__isHover) {
                    e.dispatchEvent({
                        type: 'mouseover'
                    });
                    e.__isHover = true;
                }
            } else {
                if (e.__isHover) {
                    e.dispatchEvent({
                        type: 'mouseout'
                    });
                    e.__isHover = false;
                }
            }
        });
    }

    movePen() {
        const layers = this.layers;
        const mouse = this.mouse;
        const { startOffsetX, startOffsetY } = this.mainCanvas;

        const pen = layers._sel

        if (pen instanceof Pencil) {

            if (pen._drag) {
                pen.setPosition(mouse.x - this._offsetX - startOffsetX, mouse.y - this._offsetY - startOffsetY);
            }


        }
    }

    startDragPen() {
        const layers = this.layers;
        const mouse = this.mouse;
        const { startOffsetX, startOffsetY } = this.mainCanvas;

        const pen = layers._sel;


        if (pen instanceof Pencil) {
            let mousePos = pen.rubber.mouseIn(mouse.x - startOffsetX, mouse.y - startOffsetY);
            if (mousePos == 'center') {

                console.log('pen start drag');

                pen._drag = true;

                this._offsetX = mouse.x - pen.rubber.rect.x - startOffsetX;
                this._offsetY = mouse.y - pen.rubber.rect.y - startOffsetY;

            }
            else {
                layers.unSelectAll();
                this.editor.signals.objectSelected.dispatch(null);
            }
        } else {
            layers.unSelectAll();
            this.editor.signals.objectSelected.dispatch(null);
        }

    }

    // 触发选中元素的事件
    eventHandle(eventType, nativeEvent) {
        let cur = this.findHoverElement();
        if (cur != null && cur instanceof EventDispatcher) {
            cur.dispatchEvent({ type: eventType, nativeEvent: nativeEvent });
        }
    }

    // 在画布上新建一个矩形图层
    addRect() {
        // const { context, startOffsetX, startOffsetY } = this.mainCanvas;
        const mouse = this.mouse;
        const layers = this.layers;

        let rect = this.rect = new Rect(mouse.x, mouse.y, 0, 0);

        layers.add(rect);
        this.select(rect);

        // rect event
        rect.addEventListener('mousedown', (evt) => {
            this.select(rect);
            this.handleResizeElement(evt.target);
        });

        rect.addEventListener('mouseover', (evt) => {
            console.log('rect mouseover');
            layers.setHover(evt.target);
        });

        rect.addEventListener('mouseout', (evt) => {
            console.log('rect mouseout');

            this.layers.removeHover(evt.target);
        });

        rect.addEventListener('contextmenu', (evt) => {
            console.log('contextmenu', evt.target);

        });

    }

    // 在画布上绘制矩形
    setupRect(x, y, width, height) {
        if (this.rect) {
            this.rect.setup(x, y, width, height);
            this.editor.signals.objectChanged.dispatch(this.rect);
        }
    }

    setStartOffset(ox, oy) {
        this.mainCanvas.startOffsetX = ox;
        this.mainCanvas.startOffsetY = oy;
    }

    // 在画布上新建一个画笔图层
    addPen() {
        const layers = this.layers;
        let pen = this.pen = new Pencil();

        // pencil event
        pen.addEventListener('mousedown', (evt) => {
            // layers.selectElement(evt.target);
            this.handleResizeElement(evt.target);
            this.select(evt.target);
        });

        pen.addEventListener('mouseover', (evt) => {
            layers.setHover(evt.target);
        });

        pen.addEventListener('mouseout', (evt) => {
            layers.removeHover(evt.target);
            console.log('mouseout');
        });

        layers.unSelectAll();
        layers.add(pen);
    }

    // 在画布上添加一个段落
    addTextPragraph() {
        const layers = this.layers;
        const { mouse, mainCanvas: { context, startOffsetX, startOffsetY } } = this;

        if (this.textParagraph) {
            this.textParagraph.textCursor = null;
        }

        let act = this.findActiveParagraph();

        if (act != null) {
            console.log('actParagraph', act);
            this.textParagraph = act;
            this.textParagraph.setForceState('normal')
            this.textParagraph.textCursor = new TextCursor(act.x + startOffsetX, act.y + startOffsetY);
            this.textParagraph.setCaretByPos(context, mouse.x - startOffsetX, mouse.y - startOffsetY);
        } else {

            let p = this.textParagraph = new TextParagraph(mouse.x + startOffsetX, mouse.y + startOffsetY);

            p.addEventListener('mousedown', (evt) => {
                this.handleResizeElement(evt.target);
                this.select(evt.target);
            });

            p.addEventListener('mouseover', (evt) => {
                console.log('mouseover');
                layers.setHover(evt.target);
            })

            p.addEventListener('mouseout', (evt) => {
                console.log('mouseout');
                layers.removeHover(evt.target);
            })
            this.layers.add(p);
        }




    }

    // 选择画布上的一个元素
    select(element) {
        this.layers.selectElement(element);
        this.editor.signals.objectSelected.dispatch(element);
    }

    findActiveParagraph() {
        const { layers, mouse, mainCanvas: { context, startOffsetX, startOffsetY } } = this;

        let paragraph = null;

        layers.traverse(elem => {
            if (elem instanceof TextParagraph) {
                let inParagraph = elem.isPointInside(context, mouse.x - startOffsetX, mouse.y - startOffsetY)
                // 查找
                if (inParagraph) {
                    paragraph = elem
                }
            }
        });

        return paragraph
    }

    showSelectBox() {
        const { startSelectMouseDownX, startSelectMouseDownY, mouse, mainCanvas: { context, startOffsetX, startOffsetY } } = this;

        if (!this.selectBox) {
            this.selectBox = new Rect()
            this.selectBox.stroke = '#30B7FC';
            this.selectBox.fill = 'rgba(0,0,0,0)';
            this.selectBox.lineWidth = 1;
            this.layers.add(this.selectBox)
        }

        const x = startSelectMouseDownX
        const y = startSelectMouseDownY
        const w = mouse.x - x
        const h = mouse.y - y
        this.selectBox.setup(x - startOffsetX, y - startOffsetY, w, h)
    }

}

export { Viewer };